﻿using System;
using System.Linq;
using UnityEngine;
using Zero;

public class ActorDamageable : MonoBehaviour
{
    private ElementHolder _elementHolder;

    public event Action<TakeDamageInfo> OnTakeDamage;

    private void Awake()
    {
        _elementHolder = GetComponent<ElementHolder>();
    }
    
    public void TakeDamage(AttackData attackData)
    {
        var ownerStats = GetComponent<ActorStats>();
        var sourceStats = attackData.Source.GetComponent<ActorStats>();

        // 是否已死亡
        if (ownerStats.Health <= 0f)
        {
            return;
        }

        // 是否闪避
        if (PCRFormula.IsMiss(ownerStats.Evasion, sourceStats.Hit))
        {
            CreateMissText();
            return;
        }
        
        // 检测元素反应
        var triggerElementData = new TriggerElementData
        {
            Source = attackData.Source,
            ElementType = attackData.ElementType,
            Gauge = attackData.ElementGauge,
            StrikeType = attackData.StrikeType
        };
        _elementHolder.ElementReaction(triggerElementData);

        // 计算伤害
        var finalDamage = PCRFormula.CalculateDamage(attackData.Damage, ownerStats.Defense);

        // 是否暴击
        var isCritical = PCRFormula.IsCritical(ownerStats.Level, sourceStats.Level, sourceStats.Critical);

        // 如果暴击，伤害乘以暴击伤害倍率
        if (isCritical)
        {
            finalDamage *= sourceStats.CriticalDamage;
        }

        // 伤害增幅取在同时触发下取最大的一方
        if (triggerElementData.DamageBoosters.Count > 0)
        {
            finalDamage *= triggerElementData.DamageBoosters.Max(x => x.Value);
        }

        // 扣除血量
        ownerStats.Health -= finalDamage;
        var takeDamageInfo = new TakeDamageInfo
        {
            Source = attackData.Source,
            Target = gameObject,
            FinalDamage = finalDamage,
            ElementType = attackData.ElementType,
            StrikeType = attackData.StrikeType,
            IsKill = ownerStats.Health <= 0f,
            IsCritical = isCritical
        };
        OnTakeDamage?.Invoke(takeDamageInfo);

        // 创建伤害数字
        CreateWorldNumber(takeDamageInfo);
    }

    private void CreateMissText()
    {
        ServiceLocator.Get<IWorldNumberService>().CreateMiss(transform.position);
    }

    private void CreateWorldNumber(TakeDamageInfo takeDamageInfo)
    {
        ServiceLocator.Get<IWorldNumberService>().CreateDamage(
            new DamageNumberInfo
            {
                ElementType = takeDamageInfo.ElementType,
                Amount = takeDamageInfo.FinalDamage,
                IsCritical = takeDamageInfo.IsCritical,
                Position = transform.position
            }
        );
    }

    public void TakeHeal(float amount, GameObject source)
    {
        var stats = GetComponent<ActorStats>();
        stats.Health += amount;
        ServiceLocator.Get<IWorldNumberService>().CreateHeal(new HealNumberInfo
        {
            Amount = amount,
            Position = transform.position
        });
    }
}